


#include "CelBackground.h"

// Uses global graphics client, scene and config objects

CelBackground::CelBackground(D3D11Client *pGC) :
    mGraphicsClient(pGC),
    mScene(pGC->getScene()),
    mConfig(pGC->getConfig()),
    D3D11Effect() {

    tbuf = TileManager::tbuf;
    TPL = TileManager::TPL;
    NLAT = TileManager::NLAT;
    NLNG = TileManager::NLNG;
    patchidx = TileManager::patchidx;

    InitCelBackgroundEffect();

    char *tmp = (char*)mGraphicsClient->GetConfigParam( CFGPRM_CSPHERETEXTURE );
    if( !tmp[0] ) {
        disabled = true;
        return;
    }
    else {
        disabled = false;
        strcpy( texname, tmp );
    }

    Intensity = (float)( *(double*)mGraphicsClient->GetConfigParam( CFGPRM_CSPHEREINTENS ) );

    maxlvl = 8;
    maxbaselvl = 8;
    int maxidx = patchidx[maxbaselvl];
    tiledesc = new TILEDESC [maxidx];
    memset( tiledesc, 0, sizeof(TILEDESC)*maxidx );

    LoadTextures();

    MATRIX3 R = {    2000, 0, 0,
                    0, 2000, 0,
                    0, 0, 2000    };

    double theta = 60.18*RAD;
    double phi = 90.02*RAD;
    double lambda = 173.6*RAD;
    double sint = sin( theta ), cost = cos( theta );
    double sinp = sin( phi ), cosp = cos( phi );
    double sinl = sin( lambda ), cosl = cos( lambda );

    ecl2gal = _M(    cosp, 0, sinp,
                    0, 1, 0,
                    -sinp, 0, cosp    );

    ecl2gal = mul( _M(    1, 0, 0,
                        0, cost, sint,
                        0, -sint, cost ), ecl2gal );

    ecl2gal = mul( _M(    cosl, 0, sinl,
                        0, 1, 0,
                        -sinl, 0, cosl ), ecl2gal );
    R = mul (ecl2gal, R);

    D3DXMatrixIdentity( &trans );
    trans._11 = float(R.m11);
    trans._12 = float(R.m12);
    trans._13 = float(R.m13);
    trans._21 = float(R.m21);
    trans._22 = float(R.m22);
    trans._23 = float(R.m23);
    trans._31 = float(R.m31);
    trans._32 = float(R.m32);
    trans._33 = float(R.m33);
}

CelBackground::~CelBackground() {
    if( disabled )
        return;

    if( ntex ) {
        for( DWORD j = 0; j < ntex; j++ )
        {
            REL( texbuf[j] );
        }
        delete [ ] texbuf;
    }
    delete [ ] tiledesc;
}

void CelBackground::SaveParams( int level, int bglvl ) {
    if( disabled )
        return;

    intens = Intensity;

    if( bglvl )
        intens *= exp( -(float)bglvl*0.05f );

    if( !intens )
        return;

    RP.tgtlvl = level = min( level, maxlvl );
    RP.mWorld = trans;
    double Aspect = (double)mConfig->cWidth/(double)mConfig->cHeight;//Scene function !
    RP.viewap = atan( Aspect*tan(oapiCameraAperture()) );

    MATRIX3 rcam;
    oapiCameraRotationMatrix( &rcam );
    rcam = mul( ecl2gal, rcam );
    RP.CamDir = _V( rcam.m13, rcam.m23, rcam.m33 );
}

void CelBackground::Render( /*int level, int bglvl*/ ) {
    if( disabled )
        return;

    const float BlendFactors[4] = { 1.0f };
    /*
    if( disabled )
        return;

    float intens = Intensity;

    if( bglvl )
        intens *= exp( -(float)bglvl*0.05f );

    if( !intens )
        return;

    D3DXCOLOR Color( intens, intens, intens, intens );
    dCtx->UpdateSubresource( cb_PS_CelBackground, 0, NULL, &Color, 0, 0 );
    dCtx->PSSetConstantBuffers( 1, 1, &cb_PS_CelBackground );

    RP.tgtlvl = level = min( (DWORD)level, maxlvl );
    RP.mWorld = trans;
    double Aspect = (double)mConfig->cWidth/(double)mConfig->cHeight;//Scene function !
    RP.viewap = atan( Aspect*tan(oapiCameraAperture()) );

    MATRIX3 rcam;
    oapiCameraRotationMatrix( &rcam );
    rcam = mul( ecl2gal, rcam );
    RP.CamDir = _V( rcam.m13, rcam.m23, rcam.m33 );
    */
    int startlvl = maxlvl;
    int hemisphere, ilat, ilng, idx;
    int nlat = NLAT[startlvl];
    int *nlng = NLNG[startlvl];
    int texofs = patchidx[startlvl-1];

    TILEDESC *td = tiledesc + texofs;
    TEXCRDRANGE range = { 0, 1, 0, 1 };

    D3DXCOLOR Color( 3 * intens, 3 * intens, 3 * intens, intens );
    dCtx->UpdateSubresource( cb_PS_CelBackground, 0, NULL, &Color, 0, 0 );
    dCtx->PSSetConstantBuffers( 1, 1, &cb_PS_CelBackground );

    dCtx->IASetInputLayout( IL_TileVertex );
    dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
    dCtx->VSSetShader( VS_CelBackground, NULL, NULL );
    dCtx->PSSetShader( PS_CelBackground, NULL, NULL );
    dCtx->PSSetSamplers( 0, 1, &SS_CelBackground );

    dCtx->OMSetBlendState( BS_SrcAlpha_Blend_0, BlendFactors, 0xFFFFFFFF );
    dCtx->RSSetState( RS_Front_Solid );

    for( hemisphere = idx = 0; hemisphere < 2; hemisphere++ ) {
        if( hemisphere )
            D3DXMatrixMultiply( &RP.mWorld, &TileManager::RSouth, &RP.mWorld );
        for( ilat = nlat-1; ilat >= 0; ilat-- ) {
            for( ilng = 0; ilng < nlng[ilat]; ilng++ ) {
                ProcessTile( hemisphere, startlvl, ilat, nlat, ilng, nlng[ilat], td+idx,
                    range, td[idx].tex );
                idx++;
            }
        }
    }
    dCtx->RSSetState( RS_Back_Solid );
}

void CelBackground::ProcessTile( int hemisphere, int lvl, int ilat, int nlat, int ilng, int nlng, TILEDESC *tile,
        const TEXCRDRANGE &range, ID3D11ShaderResourceView *tex )
{
    static const double rad0 = sqrt(2.0)*PI05;
    VECTOR3 cnt = GetTileCenter( hemisphere, ilat, nlat, ilng, nlng);
    double rad = rad0/(double)nlat;
    double adist = acos( dotp( RP.CamDir, cnt ) ) - rad;
    if( adist > RP.viewap )
        return;

    SetWorldMatrix( ilat, nlat, ilng, nlng );

    if( !IsTileInView( lvl, ilat ) )
        return;

    RenderTile( hemisphere, lvl, ilat, nlat, ilng, nlng, tile, range, tex );
}

void CelBackground::RenderTile( int hemisphere, int lvl, int ilat, int nlat, int ilng, int nlng,
        TILEDESC *tile, const TEXCRDRANGE &range, ID3D11ShaderResourceView *tex )
{
    const UINT TileVertexStride = 40, VBOffset = 0;
    TILEMESH &mesh = TPL[lvl][ilat];

    mWorld *= *mScene->GetVP();
    dCtx->UpdateSubresource( cb_VS_CelBackground, 0, NULL, &mWorld, 0, 0 );
    dCtx->VSSetConstantBuffers( 0, 1, &cb_VS_CelBackground );
    dCtx->PSSetShaderResources( 0, 1, &tex );
    dCtx->IASetVertexBuffers( 0, 1, &mesh.VB, &TileVertexStride, &VBOffset );
    dCtx->IASetIndexBuffer( mesh.IB, DXGI_FORMAT_R16_UINT, 0 );

    dCtx->DrawIndexed( mesh.nidx, 0, 0 );
}

VECTOR3 CelBackground::GetTileCenter( int hemisphere, int ilat, int nlat, int ilng, int nlng ) {
    double cntlat = PI05*( (double)ilat + 0.5 )/(double)nlat,        slat = sin( cntlat ), clat = cos( cntlat );
    double cntlng = PI2*( (double)ilng + 0.5 )/(double)nlng + PI,    slng = sin( cntlng ), clng = cos( cntlng );
    if( hemisphere )    return _V( clat*clng, -slat, -clat*slng );
    else                return _V( clat*clng,  slat,  clat*slng );
}

void CelBackground::SetWorldMatrix( int ilat, int nlat, int ilng, int nlng ) {
    D3DXMATRIX rtile;

    double lng = PI2*(double)ilng/(double)nlng + PI;
    D3DMAT_RotY( &rtile, lng );
    D3DXMatrixMultiply( &mWorld, &rtile, &RP.mWorld );
}

bool CelBackground::IsTileInView( int lvl, int ilat ) {
    TILEMESH &mesh = TPL[lvl][ilat];

    float rad = mesh.bsRad*2000.0f;
    D3DXVECTOR3 vP;
    D3DXVec3TransformCoord( &vP, &mesh.bsCnt, &mWorld );
    return mScene->IsVisibleInCamera( &vP, rad );
}

void CelBackground::LoadTextures() {
    char cpath[256];
    int j;
    for( j = 0; j < patchidx[maxbaselvl]; j++ )
        tiledesc[j].flag = 1;

    char fname[256];
    strcpy( fname, texname );
    strcat( fname, ".tex" );
    if( !mGraphicsClient->TexturePath( fname, cpath ) ) {
        texbuf = NULL;
        return;
    }

    ntex = patchidx[maxbaselvl];
    texbuf = new ID3D11ShaderResourceView *[ntex];

    if( ntex = tbuf->LoadTextures( cpath, ntex, 0, texbuf ) ) {
        while( (int)ntex < patchidx[maxbaselvl] )
            maxlvl = --maxbaselvl;
        while( (int)ntex > patchidx[maxbaselvl] )
            texbuf[--ntex]->Release();
        // not enough textures loaded for requested resolution level
        for( j = 0; j < patchidx[maxbaselvl]; j++ )
            tiledesc[j].tex = texbuf[j];
    }
    else {
        delete [ ] texbuf;
        texbuf = NULL;
    }
}
